package com.liyasong.cf.movie;

import java.io.BufferedWriter;

import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastIDSet;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;


import com.liyasong.cf.MyDataModel;
import com.liyasong.cf.MySimilarity;

public class PreComputeSimilarity {
	
	private DataModel dataModel;
	private ItemSimilarity itemSimilarity;
	private String simFile = "bookSimFile.txt";
	
	private final int configNum = 5;
	
	private Date time;
	private DateFormat df = DateFormat.getTimeInstance();
	private static int count = 0;		//记录循环次数
	
	public PreComputeSimilarity(DataModel dataModel) throws TasteException {
		this.dataModel = dataModel;
//		itemSimilarity = new PearsonCorrelationSimilarity(dataModel);
		itemSimilarity = new MySimilarity(dataModel);
	}
	
	public void compute() throws IOException, TasteException {
		BufferedWriter bw = new BufferedWriter(new FileWriter(simFile));
		LongPrimitiveIterator items = dataModel.getItemIDs();

		Set<ItemPair> pairSet = new LinkedHashSet<ItemPair>();
		for (Iterator<Long> iterator = items; iterator.hasNext();) {			//目录中的每一个项目i
			long itemID1 = (Long) iterator.next();
			PreferenceArray pa = dataModel.getPreferencesForItem(itemID1);
			
			//如果此item的user数少于5，就直接舍弃；
			if (pa.length() >= configNum) {
				for (Preference pre : pa) {									//访问过i的每一位用户C
					long userID = pre.getUserID();
					FastIDSet userItems = dataModel.getItemIDsFromUser(userID);
					userItems.remove(itemID1);
					for (long itemID2 : userItems) {
						if (dataModel.getNumUsersWithPreferenceFor(itemID1, itemID2) >= configNum) {
							pairSet.add(new ItemPair(itemID1, itemID2));
						}
						if ((count++)%1000000 == 0) {
							time = new Date();
							System.out.println(df.format(time) + " 第" + count + "次循环");
						}
					}
				}
			} 
				
		}
		//这里文件会被写 1，2，value   2，1，value
		for (ItemPair pair : pairSet) {
			double sim = itemSimilarity.itemSimilarity(pair.id1, pair.id2);
			if (!Double.toString(sim).equals("NaN")) {
				bw.write(pair.id1+","+pair.id2+","+(float)sim+"\n");
			}
		}
		bw.close();				//输出流用完后，记得关掉，好处多多
	}
	
	public static void main(String[] args) throws TasteException, IOException {
		PreComputeSimilarity pcs = new PreComputeSimilarity(MyDataModel.bookSmallData());
		System.out.println("程序运行阶段计时：");
		long s = System.currentTimeMillis();
//		System.out.println(pcs.df.format(pcs.time));
		pcs.compute();
//		System.out.println(pcs.dataModel.getNumUsersWithPreferenceFor(100, 200));
		long e = System.currentTimeMillis();
		
		System.out.println("100万行记录，131436个用户,21742本图书，计算其图书相似度历时："+(e-s)/60000+"mins");
	}
	
	class ItemPair {
		long id1,id2;
		
		public ItemPair(long x, long y) {
			this.id1 = x;
			this.id2 = y;
		}
		
		
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + (int) (id1 ^ (id1 >>> 32))+(int) (id2 ^ (id2 >>> 32));
//			result = prime * result + (int) (id2 ^ (id2 >>> 32));
			return result;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ItemPair other = (ItemPair) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (id1 == other.id1 && id2 == other.id2)
				return true;
			if (id1 == other.id2 && id2 == other.id1)
				return true;
			return false;
		}


		private PreComputeSimilarity getOuterType() {
			return PreComputeSimilarity.this;
		}
	}
}
